package com.ztesoft.zsmart.zcm.dialing.util;

import com.ztesoft.zsmart.core.exception.BaseAppException;
import com.ztesoft.zsmart.core.log.ZSmartLogger;
import com.ztesoft.zsmart.core.util.JsonUtil;
import com.ztesoft.zsmart.zcm.core.exception.ExceptionPublisher;
import com.ztesoft.zsmart.zcm.dialing.infrastructure.DialingExceptionErrorCode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public abstract class ZipUtil {
    private static final ZSmartLogger logger = ZSmartLogger.getLogger(ZipUtil.class);

    public ZipUtil() {
    }

    /**
     * 压缩指定的单个或多个文件，如果是目录，则遍历目录下所有文件进行压缩
     *
     * @param zipFileName ZIP文件名包含全路径
     * @param files       文件列表
     */
    public static void zipToPath(String zipFileName, File... files) throws IOException {
        logger.info("压缩: " + zipFileName);
        ZipOutputStream out = null;
        try {
            createDir(zipFileName);
            out = new ZipOutputStream(new FileOutputStream(zipFileName));

            for (File file : files) {
                if (null != file) {
                    zip(out, file, file.getName());
                }
            }

            logger.info("压缩完成");
        }
        finally {
            if (null != out) {
                out.close(); // 输出流关闭
            }
        }

    }

    public static boolean zip(ZipOutputStream out, String... files) throws IOException {
        try {
            for (String file1 : files) {
                File file = new File(file1);
                if (file.exists()) {
                    zip(out, file, file.getName());
                }
            }
            logger.info("压缩完成");
            return true;
        }
        catch (Exception e) {
            logger.error(e.getMessage());
        }
        finally {
            if (null != out) {
                out.close(); // 输出流关闭
            }
        }
        return false;
    }

    /**
     * 执行压缩
     *
     * @param out  ZIP输出流
     * @param f    被压缩的文件
     * @param base 被压缩的文件名
     */
    public static void zip(ZipOutputStream out, File f, String base) throws IOException {
        // 压缩目录
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            if (null != fl) {
                if (0 == fl.length) {
                    out.putNextEntry(new ZipEntry(base + File.separator)); // 创建zip实体
                }
                for (File aFl : fl) {
                    zip(out, aFl, base + File.separator + aFl.getName()); // 递归遍历子文件夹
                }
            }
        }
        else { // 压缩单个文件
            // logger.debug("base:" + base);
            FileInputStream in = null;
            BufferedInputStream bi = null;
            try {
                out.putNextEntry(new ZipEntry(base)); // 创建zip实体
                in = new FileInputStream(f);
                bi = new BufferedInputStream(in);
                int b;
                while ((b = bi.read()) != -1) {
                    out.write(b); // 将字节流写入当前zip目录
                }
            }
            finally {
                if (null != bi) {
                    bi.close(); // 输入流关闭
                }
                if (null != out) {
                    out.closeEntry(); // 关闭zip实体
                }
                if (null != in) {
                    in.close(); // 输入流关闭
                }
            }
        }

    }

    /**
     * 目录不存在时，先创建目录
     *
     * @param zipFileName
     */
    private static void createDir(String zipFileName) {
        File targetFile = new File(zipFileName);
        // 目录不存在时，先创建目录
        if (!targetFile.getParentFile().exists()) {
            boolean result = targetFile.getParentFile().mkdirs();
            logger.info("目录创建是否创建：{}", result);
        }
    }

    /**
     * 解压文件到指定目录
     *
     * @param zipPath 压缩文件路径
     * @param descDir 目标路径
     */
    public static void unZipFiles(String zipPath, String descDir) throws IOException {
        unZipFiles(new File(zipPath), descDir);
    }

    /**
     * 解压文件到指定目录
     *
     * @param zipFile 压缩文件
     * @param descDir 目标路径
     */
    public static String unZipFiles(File zipFile, String descDir) throws IOException {
        descDir = CleanPathUtil.cleanString(descDir);
        File pathFile = new File(descDir);
        if (!pathFile.exists()) {
            boolean result = pathFile.mkdirs();
            logger.info("目录创建是否创建：{}", result);
        }
        ZipFile zip = null;
        try {
            zip = new ZipFile(zipFile);
            Enumeration entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                writeZipEntryToFile(descDir, zip, entry);
            }
            logger.info("解压完毕");
        }
        finally {
            if (zip != null) {
                zip.close();
            }
        }
        return descDir;
    }

    private static void writeZipEntryToFile(String descDir, ZipFile zip, ZipEntry entry) throws IOException {
        InputStream in = null;
        OutputStream out = null;
        try {
            String zipEntryName = entry.getName();
            in = zip.getInputStream(entry);
            String outPath = (descDir + File.separator + zipEntryName).replaceAll("\\*", File.separator);
            // 判断文件全路径是否为文件夹,是则不需解压
            if (entry.isDirectory()) {
                return;
            }
            // 判断路径是否存在,不存在则创建文件路径
            File file = new File(outPath);
            if (!file.getParentFile().exists()) {
                boolean result = file.getParentFile().mkdirs();
                logger.info("目录创建是否创建：{}", result);
            }
            out = new FileOutputStream(file);
            byte[] buf1 = new byte[1024];
            int len;
            while ((len = in.read(buf1)) > 0) {
                out.write(buf1, 0, len);
            }
        }
        finally {
            if (out != null) {
                out.close();
            }
            if (in != null) {
                in.close();
            }
        }
    }
    /**
     * 上传zip文件并解压
     *
     * @param request
     * @return 解压的目录
     * @throws IOException
     */
    public static String uploadZip(HttpServletRequest request, String path) throws IOException {
        MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
        // 获取请求的参数
        Map<String, MultipartFile> fileMap = mRequest.getFileMap();
        // 项目的绝对路径
        String ctxPath = request.getSession().getServletContext().getRealPath("/");
        File file = new File(ctxPath);
        if (!file.exists()) {
            logger.info("mkdir " + file.mkdir());
        }
        Iterator<Map.Entry<String, MultipartFile>> it = fileMap.entrySet().iterator();
        // 用hasNext() 判断是否有值，用next()方法把元素取出。
        while (it.hasNext()) {
            Map.Entry<String, MultipartFile> entry = it.next();
            MultipartFile mFile = entry.getValue();
            if (mFile.getSize() != 0 && !"".equals(mFile.getName())) {
                // 通过CommonsMultipartFile的方法直接写文件
                File newUploadFile = new File(ctxPath + File.separator + mFile.getOriginalFilename());
                mFile.transferTo(newUploadFile);
                // 解压后的路径
                String targetPath = path + File.separator + getMilliDateStr();
                targetPath = ZipUtil.unZipFiles(newUploadFile, targetPath);
                return targetPath;
            }
        }
        return null;
    }

    public static String saveFile(String baseName, Object data) throws BaseAppException {
        if (data == null) {
            return null;
        }
        String absPath = FilenameUtils.normalize(baseName + getDateStr());
        String fileName = null;
        try {
            File file = new File(absPath);
            fileName = file.getName();
            if (!file.getParentFile().exists()) {
                boolean result = file.getParentFile().mkdirs();
                logger.info("目录创建是否创建：{}", result);
            }
            FileUtils.writeStringToFile(file, JsonUtil.object2Json(data));
        }
        catch (IOException e) {
            logger.error("save data to file failed,", e);
            ExceptionPublisher.publish(DialingExceptionErrorCode.SAVE_TO_FILE_FAILED, absPath);
        }
        return fileName;
    }

    public static String readFile(String fileName) throws BaseAppException {
        File file = new File(fileName);
        String fileContent = null;
        try {
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                if (null == files || files.length == 0) {
                    ExceptionPublisher.publish(DialingExceptionErrorCode.FILE_IS_EMPTY);
                }
                else {
                    fileContent = FileUtils.readFileToString(files[0]);
                }
            }
            else {
                fileContent = FileUtils.readFileToString(file);
            }
        }
        catch (IOException e) {
            logger.error("read file failed,", e);
            ExceptionPublisher.publish(DialingExceptionErrorCode.READ_FILE_FAILED, fileName);
        }
        return fileContent;
    }

    /**
     * 精确到秒
     *
     * @return
     */
    public static String getDateStr() {
        Date dt = new Date();
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        return df.format(dt);
    }

    /**
     * 到毫秒
     *
     * @return
     */
    public static String getMilliDateStr() {
        Date dt = new Date();
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        return df.format(dt);
    }

}
